/*
 * Copyright (c) 2008, Artur Emagulov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * 27.08.2008
 * platform.h
 */

#ifndef ARCH_PLATFORM_H_
#define ARCH_PLATFORM_H_

#ifndef X86
#define X86
#endif

#include <x86/mach-pc/platform.h>

#define PHYS_OFFSET			MACH_RAM_BASE				/* PHYSICAL MEMORY BASE */
// #define PHYS_RAM_SIZE		MACH_RAM_SIZE				/* Maximum size of RAM */

#ifdef CONFIG_MMU
// #define PAGE_OFFSET		0x80000000
#define PAGE_OFFSET			__page_offset
#else
#define PAGE_OFFSET		0x00000000
#endif

#define KERNEL_BASE	PAGE_OFFSET
#define KERNEL_MAX	0xffffffff
#define USER_BASE	0x00000000
#define USER_MAX	0x80000000

#define BOOT_PTE0	0x00001000
#define INT_STACK	0x00001000
#define HW_PROFILE_INFO                __boot_info_p
#define KERNEL_END                     _end
#define BOOT_STACK	0x00002800
/* The size of our stack (16KB). */
#define STACK_SIZE                      0x4000

#define TASK_STACK_SIZE		0x00000400

#define KERNEL_PGD	0x00003000

#ifdef CONFIG_MMU
#define RESERVED_BASE	0x00000000
#define RESERVED_MAX	0x00004000
#else
#define RESERVED_BASE	0x00000000
#define RESERVED_MAX	0x00003000
#endif

/* number of IRQ handlers */
#define IRQ_ENTRIES	16


/** FIXME! TODO Descriptor table setup!???
 * GDTs
 */
#define KERNEL_CS	0x08
#define KERNEL_DS	0x10
#define USER_CS		0x18
#define USER_DS		0x20
#define KERNEL_TSS	0x28

/* TODO Number of gdt entries must be set to what we want */
#define NGDTS		6

/** FIXME! TODO
 * IDTs
 */
#define NIDTS		0xFE
#define SYSCALL_INT	0xFD
#define INVALID_INT	0xFF


#ifndef __ASSEMBLY__

#include <sys/types.h>
#include <x86/arch.h>

/**
 * Segment Descriptor
 */
struct gdt_entry {
	u16 limit_low;	 /* segment limit (lsb) */
	u16 base_low;	 /* segment base address (lsb) */
	u8 base_mid;	 /* segment base address (middle) */
	u8 access;		 /* access */
	u8 granularity; /* granularity */
	u8 base_high;	 /* segment base address (msb) */
} __packed;

/**
 * Linear memory description for lgdt instruction.
 */
struct gdt_ptr {
	u16 limit;
	u32 base;
} __packed;

/**
 * Gate Descriptor
 */
struct idt_entry {
	u16 base_lo;	/* gate offset (lsb) */
	u16 selector;	/* gate segment selector */
	u8 always0;	/* stack copy count */
	u8 flags;		/* flags */
	u16 base_hi;	/* gate offset (msb) */
} __packed;

/**
 * Linear memory description for lidt instruction.
 */
struct idt_ptr
{
	u16 limit;
	u32 base;
} __packed;



/**
 * Segment size
 */
#define SIZE_32		0x40	/* 32-bit segment */
#define SIZE_16		0x00	/* 16-bit segment */
#define SIZE_4K		0x80	/* 4K limit field */

/**
 * Segment type
 */
#define ST_ACC		0x01	/* accessed */
#define ST_LDT		0x02	/* LDT */
#define ST_CALL_GATE_16	0x04	/* 16-bit call gate */
#define ST_TASK_GATE	0x05	/* task gate */
#define ST_TSS		0x09	/* task segment */
#define ST_CALL_GATE	0x0c	/* call gate */
#define ST_INTR_GATE	0x0e	/* interrupt gate */
#define ST_TRAP_GATE	0x0f	/* trap gate */

#define ST_TSS_BUSY	0x02	/* task busy */

#define ST_DATA		0x10	/* data */
#define ST_DATA_W	0x12	/* data, writable */
#define ST_DATA_E	0x14	/* data, expand-down */
#define ST_DATA_EW	0x16	/* data, expand-down, writable */

#define ST_CODE		0x18	/* code */
#define ST_CODE_R	0x1a	/* code, readable */
#define ST_CODE_C	0x1c	/* code, conforming */
#define ST_CODE_CR	0x1e	/* code, conforming, readable */

#define ST_KERN		0x00	/* kernel access only */
#define ST_USER		0x60	/* user access */

#define ST_PRESENT	0x80	/* segment present */


#define IO_BITMAP_SIZE		(65536/8 + 1)
#define INVALID_IO_BITMAP	0x8000

/**
 * Task State Segment (TSS)
 */
struct tss {
	u32 back_link;
	u32 esp0, ss0;
	u32 esp1, ss1;
	u32 esp2, ss2;
	u32 cr3;
	u32 eip;
	u32 eflags;
	u32 eax, ecx, edx, ebx;
	u32 esp, ebp, esi, edi;
	u32 es, cs, ss, ds, fs, gs;
	u32 ldt;
	u16 dbg_trace;
	u16 io_bitmap_offset;
#if 0
	u32 io_bitmap[IO_BITMAP_SIZE/4+1];
	u32 pad[5];
#endif
} __packed;

/**
 * i386 flags register
 */
#define EFL_CF		0x00000001	/* Carry */
#define EFL_PF		0x00000004	/* Parity */
#define EFL_AF		0x00000010	/* Carry */
#define EFL_ZF		0x00000040	/* Zero */
#define EFL_SF		0x00000080	/* Sign */
#define EFL_TF		0x00000100	/* Trap */
#define EFL_IF		0x00000200	/* Interrupt enable */
#define EFL_DF		0x00000400	/* Direction */
#define EFL_OF		0x00000800	/* Overflow */
#define EFL_IOPL	0x00003000	/* IO privilege level: */
#define EFL_IOPL_KERN	0x00000000	/* Kernel */
#define EFL_IOPL_USER	0x00003000	/* User */
#define EFL_NT		0x00004000	/* Nested task */
#define EFL_RF		0x00010000	/* Resume without tracing */
#define EFL_VM		0x00020000	/* Virtual 8086 mode */
#define EFL_AC		0x00040000	/* Alignment Check */

/**
 * CR0 register
 */
#define CR0_PG		0x80000000	/* enable paging */
#define CR0_CD		0x40000000	/* cache disable */
#define CR0_NW		0x20000000	/* no write-through */
#define CR0_AM		0x00040000	/* alignment check mask */
#define CR0_WP		0x00010000	/* write-protect kernel access */
#define CR0_NE		0x00000020	/* handle numeric exceptions */
#define CR0_ET		0x00000010	/* extension type is 80387 coprocessor */
#define CR0_TS		0x00000008	/* task switch */
#define CR0_EM		0x00000004	/* emulate coprocessor */
#define CR0_MP		0x00000002	/* monitor coprocessor */
#define CR0_PE		0x00000001	/* enable protected mode */

/**
 * Page table (PTE)
 */
#define PAGE_SIZE   0x1000                                          /* default page size */
// typedef u32 * page_table_t;

/**
 * Page directory entry
 */
#define PDE_PRESENT	0x00000001
#define PDE_WRITE	0x00000002
#define PDE_USER	0x00000004
#define PDE_WTHRU	0x00000008
#define PDE_NCACHE	0x00000010
#define PDE_ACCESS	0x00000020
#define PDE_SIZE	0x00000080
#define PDE_AVAIL	0x00000e00
#define PDE_ADDRESS	0xfffff000

/**
 * Page table entry
 */
#define PTE_PRESENT	0x00000001
#define PTE_WRITE	0x00000002
#define PTE_USER	0x00000004
#define PTE_WTHRU	0x00000008
#define PTE_NCACHE	0x00000010
#define PTE_ACCESS	0x00000020
#define PTE_DIRTY	0x00000040
#define PTE_AVAIL	0x00000e00
#define PTE_ADDRESS	0xfffff000

/**
 *  Virtual and physical address translation
 */
#define pgd_len()		(PAGE_SIZE / sizeof(pde_t))
#define pgt_len()		(PAGE_SIZE / sizeof(pte_t))
#define pgd_idx(virt)    (int)((((vaddr_t)(virt)) >> 22) & 0x3ff)
#define pgt_idx(virt)    (int)((((vaddr_t)(virt)) >> 12) & 0x3ff)

#define pgt_present(vpgd, virt)  (vpgd[pgd_idx(virt)] & PDE_PRESENT)

#define page_present(vpgt, virt) (vpgt[pgt_idx(virt)] & PTE_PRESENT)

#define vpgd_virt_to_vpgt(vpgd, virt) \
            (page_table_t)phys_to_virt(vpgd[pgd_idx(virt)] & PDE_ADDRESS)

#define vpgt_virt_to_page(vpte, virt) \
            (vpte[pgt_idx(virt)] & PTE_ADDRESS)

/**
 * Page mapping
 */
#ifdef CONFIG_MMU

	#define PAGE_OFFSET_ADR	&PAGE_OFFSET
	#define __pa(x)	(void *)((unsigned long)(x) - (unsigned long)PAGE_OFFSET_ADR + (unsigned long)PHYS_OFFSET)
	#define __va(x)	(void *)((unsigned long)(x) + (unsigned long)PAGE_OFFSET_ADR - (unsigned long)PHYS_OFFSET)
	#define phys_to_virt(pa)	(void *)((unsigned long)(pa) + (unsigned long)PAGE_OFFSET_ADR - (unsigned long)PHYS_OFFSET)
	#define virt_to_phys(va)	(void *)((unsigned long)(va) - (unsigned long)PAGE_OFFSET_ADR + (unsigned long)PHYS_OFFSET)
	#define bus_to_virt(pa) (void *)((unsigned long)pa + (unsigned long)V_IO_BASE - (unsigned long)P_IO_BASE)
	#define virt_to_bus(va) (void *)((unsigned long)va - (unsigned long)V_IO_BASE + (unsigned long)P_IO_BASE)

#else

	#define PAGE_OFFSET_ADR	0
	#define __pa(x)	x
	#define __va(x)	x
	#define phys_to_virt(pa)		pa
	#define virt_to_phys(va)		va
	#define bus_to_virt(pa)		pa
	#define virt_to_bus(va)		va

#endif

#endif /*__ASSEMBLY__*/

#endif /*PLATFORM_H_*/
